﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Data.SqlClient;

namespace TM_Server
{
    class Wait2Online
    {
        #region 全局变量部分

        Form1 Main_f;
        //用来接受用户连接的套接字
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);           
        public delegate void Pt();      //创建委托用于刷窗体
        TcpListener Listener;
        bool _IsListen = true;
        NetworkStream Stream;           //流对象，用于捕获用户传送信息
        String Rev_Order = "";
        String Friend_QQ = "";          //用于保存对方好友的QQ
        String Friend_Ip = "";          //保存好友IP
        String Port = "";               //用户上线端口
        String Number_MySelf = "";      //用于保存上线QQ
        String Friend_Port = "";        //保存上线好友的上线端口
        String Main_QQ = "";            //用来保存登录用户的号码
        NetworkStream Stream_For_User;  //用来保存登录用户的句柄返回

        #endregion

        #region 构造函数部分

        public Wait2Online(Form1 Main_f)
        {
            this.Main_f = Main_f;  //将主窗体句柄传递
        }

        #endregion

        #region 此方法用于监听端口，等待用户上线

        /*
         * 此方法用于监听端口，等待用户上线
         */
        public void Lis_Port()
        {
            Listener = new TcpListener(Globle._OnLinePort);  //默认上线端口：10000
            try
            {
                Globle._isRuning = 1;  //开启启动
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_TM_Run));
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_State));
                while (_IsListen)
                {
                    Listener.Start();  //一直监听
                    //尝试接受连接
                    this.socket = Listener.AcceptSocket();    //获得连接信息
                    //MessageBox.Show("有人连接了：Socket Port:" +  this.socket.RemoteEndPoint.ToString());
                    //如果此套接字已经连接上
                    if (this.socket.Connected)
                    {
                        Thread thread = new Thread(new ThreadStart(this.Rev));
                        thread.Start();
                    }
                    
                }
            }
            catch(Exception ex)
            {
                Globle._isRuning = 0;  //停止启动
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_State));
                //MessageBox.Show("端口监听出现错误：" + ex.Message);
            }
        }

        #endregion

        #region 此方法用于监听用户命令

        /*
         * 此方法用于监听用户命令
         */
        public void Rev()
        {
            while (_IsListen)
            {
                try
                {
                    byte[] bb = new byte[512];
                    //如果此套接字已经连接上
                    if (this.socket.Connected)
                    {
                        Stream = new NetworkStream(this.socket);  //获取用户流信息
                        Stream.Read(bb, 0, bb.Length);  //读取内容
                        Stream.Flush();  //清空
                        //MessageBox.Show(Encoding.Default.GetString(bb));
                        this.Rev_Order = (Encoding.Default.GetString(bb)).Replace("\0", "");  //转换成字符串
                        String[] Orders_sp = this.Rev_Order.Split(new String[] { "$$" }, StringSplitOptions.RemoveEmptyEntries);
                        //如果得到的命令不是登录后的信息传递
                        if (Orders_sp.Length <= 1)
                        {
                            String[] Orders = this.Rev_Order.Split(new String[] { "|**|" }, StringSplitOptions.RemoveEmptyEntries);
                            if (Orders.Length <= 1)
                            {
                                try
                                {
                                    String Ip = IPAddress.Parse(((IPEndPoint)this.socket.RemoteEndPoint).Address.ToString()).ToString();
                                    Rev2Client_SendMessage Rev = new Rev2Client_SendMessage(Stream, this.Rev_Order, Ip);
                                }
                                catch (Exception ex)
                                {
                                    MessageBox.Show("显示用户上线的时候出现错误：" + ex.Message);
                                }
                            }
                            //QQ注册
                            else
                            {
                                String NickName = Orders[0].Trim();  //得到昵称
                                String Pwd = Orders[1].Trim();       //得到密码
                                String Age = Orders[2].Trim();       //得到年龄
                                Sign2Member SM = new Sign2Member(NickName, Pwd, Age, Stream);  //准备写入数据库
                                String Res2Sign = SM.Sign_It();  //得到返回值
                            }
                        }
                        else
                        {
                            this.Main_QQ = Orders_sp[2];
                            //开始处理信息
                            this.Transect_Order(Orders_sp);
                        }
                        
                    }
                    else
                    {
                        //MessageBox.Show("套接字已经断开了");
                    }
                }
                catch (Exception ex)
                {
                    //MessageBox.Show("出现错误[服务器]：" + ex.Message);
                }
            }
        }

        #endregion

        #region 此方法用于停止监听端口

        /*
         * 此方法用于停止监听端口
         */
        public void Stop_Lis_Port()
        {
            try
            {
                Listener.Stop();
                //this._IsListen = false;  //关闭监听
                Globle._isRuning = 0;    //关闭启动
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_TM_Stop));
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_State));
            }
            catch(Exception ex)
            {
                MessageBox.Show("关闭服务器错误：" + ex.Message);
            }
        }

        #endregion

        #region 处理用户登陆后的信息

        /*
         * 此方法用来处理用户登陆后的信息
         */
        public void Transect_Order(String[] Sp_Orders)
        {
            //通过第一个参数，判断第二个参数，来调用不同的处理方法
            switch (Sp_Orders[0])
            {
                //修改信息命令
                case "Update":
                    //判断要修改的项目是什么
                    switch (Sp_Orders[1])
                    {
                        //修改个人签名
                        case "Pro":
                            this.UpdateToData("Pro", Sp_Orders[2], Sp_Orders[3]);
                            break;
                        //修改昵称
                        case "NickName":
                            break;
                        //修改年龄
                        case "Age":
                            break;
                        //修改个人详细信息
                        case "Info":
                            break;
                        //更新上线端口
                        case "Port":
                            //将QQ,上线端口传入
                            this.Port = Sp_Orders[3];
                            this.Number_MySelf = Sp_Orders[2];
                            this.Update2Port(Sp_Orders[2], Sp_Orders[3]);
                            break;
                            //其他
                        default:
                            return;  
                    }
                    break;
                //聊天命令
                case "Chk_Online":
                    this.Chk_User_Online(Sp_Orders[1]);  //调用检测用户上线方法
                    break;
                //其他情况
                default:
                    break;
            }
        }

        #endregion

        #region 根据用户信息的修改类型来修改相应的数据库信息

        /*
         * 此方法用于根据用户信息的修改类型来修改
         * 相应的数据库信息
         */
        private void UpdateToData(String type , String QQ , String Content)
        {
            String CMD;
            //根据修改类型的不同来拼接SQL字符
            switch (type)
            {
                //修改个人签名
                case "Pro":
                    CMD = "UPDATE TM_User SET User_Pro = '" + Content + "' WHERE User_QQ_Number = " + QQ;
                    this.RunToDataBase(CMD);  //执行此命令
                    break;
                //修改昵称
                case "NickName":
                    CMD = "UPDATE TM_User SET User_NickName = '" + Content + "' WHERE User_QQ_Number = " + QQ;
                    this.RunToDataBase(CMD);  //执行此命令
                    break;
                //修改年龄
                case "Age":
                    CMD = "UPDATE TM_User SET User_Age = '" + Content + "' WHERE User_QQ_Number = " + QQ;
                    this.RunToDataBase(CMD);  //执行此命令
                    break;
                //修改个人详细信息
                case "Info":
                    CMD = "UPDATE TM_User SET User_Info = '" + Content + "' WHERE User_QQ_Number = " + QQ;
                    this.RunToDataBase(CMD);  //执行此命令
                    break;
                //其他
                default:
                    return;
            }
        }

        #endregion

        #region 执行数据库命令

        /*
         * 此方法用于执行数据库命令
         */
        private void RunToDataBase(String CMD)
        {
            try
            {
                using (SqlConnection Conn = new SqlConnection("Data Source=(local);Initial Catalog=TM_Setting_Infomation;Persist Security Info=True;User ID=sa;Password=songna0917"))
                {
                    Conn.Open();  //打开数据库
                    using (SqlCommand Command = new SqlCommand(CMD, Conn))
                    {
                        try
                        {
                            Command.ExecuteNonQuery();
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("修改用户个人签名错误：" + ex.Message);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("更新信息到数据库时发生错误：" + ex.Message);
            }
        }

        #endregion

        #region 检测用户请求的好友上线状态

        /*
         * 此方法用于检测用户请求的好友上线状态
         */
        private void Chk_User_Online(String QQ)
        {
            this.Friend_QQ = QQ;
            Globle.Main_f.BeginInvoke(new Pt(this.Chk_User_delegate));  //用委托来实现查询用户
        }

        /*
         * 此方法通过委托来查询目标用户的在线情况
         */
        public void Chk_User_delegate()
        {
            int Sg = 0;         //标志位，表示是否找到用户     0 | 没找到，1 | 找到了
            int Stream_Sg = 0;  //数据流标志位，表示是否找到用户     0 | 没找到，1 | 找到了
            for (int i = 0; i < Globle.Main_f.User_Show.Items.Count; i++)
            {
                if (Globle.Main_f.User_Show.Items[i].Text == this.Friend_QQ)
                {
                    Sg = 1;  //找到了
                    this.Friend_Port = Globle.Main_f.User_Show.Items[i].SubItems[2].Text.Trim();  //得到端口
                    this.Friend_Ip = Globle.Main_f.User_Show.Items[i].SubItems[1].Text.Trim();  //得到Ip
                    //return;
                }
            }
            if (Globle.list != null)
            {
                for (int i = 0; i < Globle.list.Count; i++)
                {
                    //根据登陆的QQ，来查找相应的数据流
                    if (Globle.list[i].Tm_number == this.Main_QQ)
                    {
                        this.Stream_For_User = Globle.list[i].Stream;
                        Stream_Sg = 1;  //找到了
                    }
                }
            }
                //没找到的话
                if (Sg == 0)
                {
                    //如果Socket可以连接
                    if (this.socket.Connected)
                    {
                        //传递给客户端未上线信息
                        this.Stream.Write(Encoding.Default.GetBytes("0||None"), 0, Encoding.Default.GetBytes("0||None").Length);
                        this.Stream.Flush();
                    }
                    else
                    {
                        MessageBox.Show("好友未找到，要发送的时候，SOCKET却是断开的！");
                    }
                }
                else
                {
                    //如果Socket可以连接
                    if (this.socket.Connected)
                    {
                        //传递给客户端上线信息
                        this.Stream.Write(Encoding.Default.GetBytes("1||" + this.Friend_Port + "||" + this.Friend_Ip), 0, Encoding.Default.GetBytes("1||" + this.Friend_Port + "||" + this.Friend_Ip).Length);
                        this.Stream.Flush();
                    }
                    else
                    {
                        MessageBox.Show("好友找到了！要发送的时候，SOCKET却是断开的！");
                    }
                }
        }

        #endregion

        #region 用于更新用户上线的端口 [默认为：4000 ，随机值：4001~4005]

        /*
         * 此方法用于更新用户上线的端口
         * 默认为：4000 ，随机值：4001~4005
         */
        public void Update2Port(String QQ , String Port)
        {
            //调用委托，更新端口
            Globle.Main_f.BeginInvoke(new Pt(this.Update_Port_delegate));
        }

        /*
         * 此方法用于刷新用户上线端口 [委托]
         */
        public void Update_Port_delegate()
        {
            int flag = 0;
            for (int i = 0; i < Globle.Main_f.User_Show.Items.Count; i++)
            {
                if (Main_f.User_Show.Items[i].Text == this.Number_MySelf)
                {
                    //更新端口
                    Globle.Main_f.User_Show.Items[this.Number_MySelf].SubItems[2].Text = this.Port;
                    flag = 1;
                    return;
                }
            }
            if (flag == 0)
            {
                MessageBox.Show("没找到要更新的用户[端口]：" + this.Number_MySelf);
            }
        }

        #endregion
    }
}
